/*
 * 区域基础函数
 */

#include <stdlib.h>
#include <string.h>
#include "gui.h"

struct Area _gui_root = {
	0, 0, 800, 600,
	1, 0,		//参与检测, 但不渲染
	NULL, NULL,	//没有图像
	0, NULL,	//没有焦点, 用户数据为null
	NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL,
	NULL,		//没有子区域
	NULL		//兄弟区域永远为NULL
};

static struct Area *key_event_area = NULL;	//接收键盘事件的区域

void area_focus(struct Area *a)
{
	key_event_area = a;
}

/*	新建一块区域, 如果father为null, 则从根区域中创建
 *	返回区域指针, 新生成区域除了坐标大小外, 其它数据全为0
 */

struct Area *area_new(struct Area *father, int x, int y, int width, int height)
{
	if (father == NULL) father = &_gui_root;
	struct Area *ret;
	
	ret = malloc(sizeof(struct Area));
	memset(ret, 0, sizeof(struct Area));
	
	ret -> _x = x;
	ret -> _y = y;
	ret -> _width = width;
	ret -> _height = height;
	
	if (father -> _child == NULL) {			//为第一个子区域
		father -> _child = ret;
	} else {					//从头部插入
		ret -> _next = father -> _child;
		father -> _child = ret;
	}
	return ret;
}

/*	从区域树中删除子树, 如果father 为null, 则从根中删除
 *	如果area为null, 则删除所有子树, 删除一个区域将连带删除该区域的子区域
 */
void area_delete(struct Area *father, struct Area *area)
{
	if (father == NULL) {
		area_delete(&_gui_root, area);
		return;
	}
	if (area == NULL) {
		struct Area *bros = father -> _child;
		struct Area *tmp;
		while(bros) {
			tmp = bros -> _next;
			area_delete(father, bros);
			bros = tmp;
		}
		return;
	}
	struct Area *bros = father -> _child;
	struct Area *tmp;
	
	if (key_event_area == area) {	//销毁焦点区域
		key_event_area = NULL;
	}
	
	if (father -> _child == area) {
		tmp = father -> _child;
		father -> _child = tmp -> _next;
		free(tmp);
	} else {
		while(bros -> _next) {
			if (bros -> _next == area) {
				tmp = bros -> _next;
				bros -> _next = bros -> _next -> _next;
				free(tmp);
				break;
			}
			bros = bros -> _next;
		}
	}
}


static struct Area *_a_detect_child(struct Area *fa, int x, int y)
{
	struct Area *tmp = fa -> _child;
	
	while (tmp) {
		if (x > tmp -> _x &&
		    x < tmp -> _x + tmp -> _width &&
		    y > tmp -> _y &&
		    y < tmp -> _y + tmp -> _height &&
		    tmp -> enable) {
			return tmp;
		}
		tmp = tmp -> _next;
	}
	return NULL;
}

static struct Area *area_detect(struct Area *a_det, int x, int y)
{
	struct Area *tmp = &_gui_root;
	struct Area *t;
	struct Area *ret = &_gui_root;
	if (a_det) {
		tmp = a_det;
		ret = a_det;
	}
	if (x < tmp -> _x ||
		x > tmp -> _x + tmp -> _width ||
		y < tmp -> _y ||
		y > tmp -> _y + tmp -> _height ||
		(!tmp -> enable)) {
		return NULL;
	}
	while (tmp) {
		t = _a_detect_child(tmp, x, y);
		if (t == NULL) {
			ret = tmp;
			break;
		} else tmp = t;
	}
	if (ret == &_gui_root) return NULL;
	return ret;
}

void gui_event_idle(struct Area *a_det)
{
	int even_t;
	struct Area *active;
	int mx, my, button;
	static struct Area *last_one = NULL;
	
	///TODO:区域鼠标按键up down事件完善
	
	while ((even_t = ctrl_do_event())) {
		switch (even_t) {
			case SDL_MOUSEMOTION:
				ctrl_get_mouse(&mx, &my);
				active = area_detect(a_det, mx, my);
				if (active != last_one) {	//检测enter,leave
					if (last_one && last_one -> mouse_leave && last_one -> enable) {
						(*last_one -> mouse_leave)(last_one,
							last_one -> userdata);
					}
					if (active && active -> mouse_enter && active -> enable) {
						(*active -> mouse_enter)(active,
							active -> userdata);
					}
					last_one = active;
				}
				if (active && active -> mouse_move && active -> enable) {
					(*active -> mouse_move)(active, mx - active -> _x,
						my - active -> _y, active -> userdata);
				}
				break;
			case SDL_MOUSEBUTTONDOWN:
				ctrl_get_mouse(&mx, &my);
				button = ctrl_get_botton();
				active = area_detect(a_det, mx, my);
				if (active && active -> mouse_down && active -> enable) {
					(*active -> mouse_down)(active, button, 
						mx - active -> _x,
						my - active -> _y, active -> userdata);
				}
				
				key_event_area = active;
				
				break;
			case SDL_MOUSEBUTTONUP:
				ctrl_get_mouse(&mx, &my);
				button = ctrl_get_botton();
				active = area_detect(a_det, mx, my);
				if (active && active -> mouse_up && active -> enable) {
					(*active -> mouse_up)(active, button, 
					mx - active -> _x,
					my - active -> _y, active -> userdata);
				}
				break;
			case SDL_KEYDOWN:
				if (key_event_area && key_event_area -> key_down) {
					(*key_event_area -> key_down)(key_event_area,
						ctrl_get_key(), ctrl_get_unicode(),
						key_event_area -> userdata);
				}
				break;
			case SDL_KEYUP:
				if (key_event_area && key_event_area -> key_up) {
					(*key_event_area -> key_up)(key_event_area,
						ctrl_get_key(), ctrl_get_unicode(),
						key_event_area -> userdata);
				}
				break;
			default:
				break;
		}
	}
}

static void _gui_render(struct Area *a);

static void _resev_render(struct Area *bros)
{
	if (bros == NULL) return;
	if (bros -> _next) {
		_resev_render(bros -> _next);
	}
	if (bros -> visual) {
	//	video_set_color(0, 0, 255);
	//	video_rectangle(bros -> _x - 3, bros -> _y - 3,
	//			bros -> _x + bros -> _width + 3,
	//			bros -> _y + bros -> _height + 3);
		if (bros -> user_render) {
			(*bros -> user_render)(bros, bros -> userdata);
		}
		_gui_render(bros);
	}
}

static void _gui_render(struct Area *a)
{
	struct Area *bros = a -> _child;
	_resev_render(bros);
	/*
	while (bros) {
		if (bros -> visual) {
			video_set_color(0, 0, 255);
			video_rectangle(bros -> _x - 3, bros -> _y - 3,
					bros -> _x + bros -> _width + 3,
					bros -> _y + bros -> _height + 3);
			if (bros -> user_render) {
				(*bros -> user_render)(bros, bros -> userdata);
			}
			_gui_render(bros);
		}
		bros = bros -> _next;
	}
	*/
}

void gui_render()
{
	video_start_draw();
	video_clear();
	
	_gui_render(&_gui_root);
	video_end_draw();
}

void area_move(struct Area *a, int dx, int dy)
{
	a -> _x += dx;
	a -> _y += dy;
	struct Area *bros = a -> _child;
	while (bros) {
		area_move(bros, dx, dy);
		bros = bros -> _next;
	}
}

/*
void visit_area(struct Area *a)
{
	struct Area *bros = a -> _child;
	while (bros) {
		printf("%d\n", bros -> _x);
		visit_area(bros);
		bros = bros -> _next;
	}
}
int main()
{
	struct Area *a1;
	struct Area *a2;
	struct Area *a3;
	struct Area *a4;
	struct Area *a5;
	a1 = area_new(NULL, 1, 2, 3, 4);
	a2 = area_new(NULL, 2, 4, 5, 6);
	a3 = area_new(a1, 3, 0, 0, 0);
	a4 = area_new(a1, 4, 6, 7, 8);
	a5 = area_new(a4, 5, 10, 10, 11);
	area_delete(a4, NULL);
	a5 = area_new(a1, 5, 10, 10, 11);
	a5 = area_new(a1, 5, 10, 10, 11);
	
	visit_area(&_gui_root);
	return 0;
	
}
*/

